#include "ata_pio.h"
#include <system/mod.h>
#include <klibc/ctype.h>

/*
 * http://wiki.osdev.org/ATA_PIO_Mode
 */

module_t ata_pio_module =
{
	.name = "ata_pio\0",
	.author = "dak\0",
	.license = "gpl\0",
	.desc = "ata pio mode driver\0",
	.dep = "\0",
	.type = MOD_TYPE_DEV_STORAGE,
	
	.init = ata_pio_init,
	.shut = ata_pio_shut,
};


/*
 * Initialize module
 */
uint32_t ata_pio_init()
{
	ata_pio_reset();
	
	ata_pio_module.dev = (vfs_node_t *) devfs_mk_node("hda\0", VFS_NODE_TYPE_DCHAR);;
	ata_pio_module.dev->read = ata_pio_aread;
	ata_pio_module.dev->write = ata_pio_awrite;
	
	ata_pio_module.dev = (vfs_node_t *) devfs_mk_node("hdb\0", VFS_NODE_TYPE_DCHAR);
	ata_pio_module.dev->read = ata_pio_aread;
	ata_pio_module.dev->write = ata_pio_awrite;
	
	
	return MOD_OK;
}


/*
 * Stopping module
 */
uint32_t ata_pio_shut()
{
	devfs_dl_node("hda\0");
	devfs_dl_node("hdb\0");
	return MOD_OK;
}


/*
 * Resetting
 */
void ata_pio_reset()
{
	
}


void ata_pio_test()
{
	mod_initm(mod_get("ata_pio"), NULL);
	ata_pio_write(1, 3, "loool\0", 5);
	
	char loll[255];
	ata_pio_read(1, 3, loll, 5);
	
	printf("%s\n", loll);	
}

/*
 * 
 */
uint32_t ata_pio_open()
{

}


uint32_t ata_pio_close()
{
	
}

/*
 * Esegue le operazioni per direzionare la lettura o scrittura
 */
uint32_t ata_pio_where(uint8_t drive, uint32_t numblock, uint64_t sector)
{
        io_outb(0x1F1, 0x00);      /* NULL byte to port 0x1F1 */
        io_outb(0x1F2, numblock);     /* Sector count */
        io_outb(0x1F3, (unsigned char) sector);  /* Low 8 bits of the block address */
        io_outb(0x1F4, (unsigned char) (sector >> 8));   /* Next 8 bits of the block address */
        io_outb(0x1F5, (unsigned char) (sector >> 16));  /* Next 8 bits of the block address */

        /* Drive indicator, magic bits, and highest 4 bits of the block address */
        io_outb(0x1F6, 0xE0 | (drive << 4) | ((numblock >> 24) & 0x0F));

        return 0;
}


/*
 * Unica read per tutti i drive
 */
uint32_t ata_pio_aread(struct vfs_node_s *node, char *data, uint32_t size, uint64_t nb)
{
	uint32_t drive;
	if(strcmp(node->name, "hda\0") == 0) drive = 0;
	else if(strcmp(node->name, "hdb\0") == 0) drive = 1;
	else return 0;
	
	return ata_pio_read(drive, nb, data, size);
}

/*
 * Unica write per tutti i drive
 */
uint32_t ata_pio_awrite(struct vfs_node_s *node, char *data, uint32_t size, uint64_t nb)
{
	uint32_t drive;
	if(strcmp(node->name, "hda\0") == 0) drive = 0;
	else if(strcmp(node->name, "hdb\0") == 0) drive = 1;
	else return 0;
	
	return ata_pio_write(drive, nb, data, size);
}


/*
 * Read from ata
 */
uint32_t ata_pio_read(uint8_t drive, uint64_t numblock, char *buf, uint32_t size)
{
        uint16_t tmpword;
        int32_t idx;

        ata_pio_where(drive, numblock, size);
        io_outb(0x1F7, 0x20);

        /* Wait for the drive to signal that it's ready: */
        while (!(io_inb(0x1F7) & 0x08));

        for (idx = 0; idx < 256 * size; idx++)
        {
                tmpword = io_inw(0x1F0);
                buf[idx * 2] = (unsigned char) tmpword;
                buf[idx * 2 + 1] = (unsigned char) (tmpword >> 8);
        }

        return size;
}


/*
 * Write data on ata
 */
uint32_t ata_pio_write(uint8_t drive, uint64_t numblock, char *buf, uint32_t size)
{
        uint16_t tmpword;
        int32_t idx;

		ata_pio_where(drive, numblock, size);
        io_outb(0x1F7, 0x30);

        /* Wait for the drive to signal that it's ready: */
        while (!(io_inb(0x1F7) & 0x08));

        for (idx = 0; idx < 256 * size; idx++)
        {
                tmpword = (buf[idx * 2 + 1] << 8) | buf[idx * 2];
                io_outw(0x1F0, tmpword);
        }

        return size;
}
